﻿package com.jfdesgagne.traklib {
	import com.jfdesgagne.traklib.collections.TrakConfiguration;
	import com.jfdesgagne.traklib.events.TrakColorEvent;
	import com.jfdesgagne.traklib.events.TrakMultiEvent;
	import com.jfdesgagne.traklib.events.TrakEvent;
	import com.jfdesgagne.traklib.geom.TrakColourMatrix;
	import com.jfdesgagne.traklib.statics.TrakTypes;
	import com.jfdesgagne.traklib.tracker.AbstractTrak;
	import com.jfdesgagne.traklib.tracker.TrakColor;
	import com.jfdesgagne.traklib.tracker.TrakMotion;
	import com.jfdesgagne.traklib.ui.TrakUI;
	import com.jfdesgagne.traklib.ui.events.TrakUIEvent;

	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObjectContainer;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.filters.ColorMatrixFilter;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.utils.Timer;

	public class Traklib extends EventDispatcher {
		protected var __output:Bitmap;
		protected var __source:Bitmap;
		protected var __video:BitmapData;
		protected var __matrix:TrakColourMatrix;
		protected var __lastTargetPosition:Point;
		protected var __frameRate:int;
		protected var __cam:Camera;
		protected var __vid:Video;
		protected var __trakUI:TrakUI;
		protected var __timer:Timer;
		protected var __refStage:DisplayObjectContainer;
		protected var __trackingType:String = TrakTypes.MOTION_TRACKING;
		protected var __tracker:AbstractTrak;
		protected var __distance:Boolean = false;
		protected var __debug:Boolean = false;

		public function Traklib(_refStage:DisplayObjectContainer, _frameRate:int = 45) {
			/*** SETUP ***/
			this.__frameRate = _frameRate;
			this.__lastTargetPosition = new Point();
			this.__refStage = _refStage;
			
			this.init();
		}

		public function get distance():Boolean {
			return this.__distance;
		}

		protected function setTrackingType(_trackingType:String):void {
			this.stopTracking();
		
			if(this.__tracker) {
				this.__tracker.destroy();
				this.__tracker = null;
			}
			
			this.__trackingType = _trackingType;
			
			switch(this.__trackingType) {
				case TrakTypes.MOTION_TRACKING:
					this.__tracker = new TrakMotion(this.__vid, this.__refStage);
					break;
				
				case TrakTypes.COLOR_TRACKING:
					this.__tracker = new TrakColor(this.__vid, this.__refStage.stage);
					//this.__tracker.addEventListener(FuelDebugEvent.TESTIMAGE, this.testImage);
					this.__tracker.addEventListener(TrakColorEvent.COLOR_CHANGED, this.changeColor);
					this.__tracker.addEventListener(TrakColorEvent.COUNTDOWN, this.updateColorCountdown);
					this.__trakUI.colorDetection = true;
					break;
			}
			
			this.__tracker.flipInput = true;
			
			this.__tracker.addEventListener(TrakMultiEvent.CREATE, this.redispatchEvent);			this.__tracker.addEventListener(TrakMultiEvent.DISAPPEAR, this.redispatchEvent);			this.__tracker.addEventListener(TrakMultiEvent.MOVE, this.redispatchEvent);
					
			
			// Show the image the MotionTracker is processing and using to track
			this.__output = new Bitmap(this.__tracker.trackingImage);
			this.__trakUI.output = this.__output;
			this.__trakUI.source = this.__source;
			this.__trakUI.reset();
			
			this.startTracking();
		}
		
		private function redispatchEvent(e:TrakMultiEvent):void {
			var area:Rectangle = e.area.clone();
			area.x = (area.x / this.__output.width) * this.__refStage.stage.stageWidth;			area.y = (area.y / this.__output.height) * this.__refStage.stage.stageHeight;
			
			this.dispatchEvent(new TrakMultiEvent(e.type, e.id, area));
			this.outputDebug("[ Traklib :: redispatchEvent] :"+area);
		}

		public function set trackingType(_val:String):void {
			this.setTrackingType(_val);
			this.__trakUI.trackingType = this.__trackingType;
		}

		protected function updateColorCountdown(e:TrakColorEvent):void {
			this.__trakUI.updateCountdown(e.color);
		}

		protected function changeColor(e:TrakColorEvent):void {
			this.__trakUI.colorDetected = e.color;
		}

		protected function initCamera():void {
			this.__cam = Camera.getCamera();
			this.__cam.setMode(this.__trakUI.sourceWidth, this.__trakUI.sourceHeight, this.__frameRate);
			
			this.__vid = new Video(this.__trakUI.sourceWidth, this.__trakUI.sourceHeight);
			this.__vid.attachCamera(this.__cam);
		}

		protected function initUI():void {
			this.outputDebug("[ Traklib :: initUI]");
			this.__trakUI = new TrakUI();
			this.__trakUI.addEventListener(TrakUIEvent.VALUE_CHANGED, this.valuesChanged);			this.__trakUI.addEventListener(TrakUIEvent.TRACKING_CHANGED, this.trackingChanged);			this.__trakUI.addEventListener(TrakUIEvent.CALL_FUNCTION_ON_TRACKER, this.callFunctionOnTracker);
		}

		private function callFunctionOnTracker(e:TrakUIEvent):void {
			switch(e.valueType) {
				case TrakColor.CALL_CHANGE_COLOR:
					TrakColor(this.__tracker).changeColor();
					break;
				case TrakColor.CALL_ADD_COLOR:
					TrakColor(this.__tracker).addColor();
					break;
			}
			
			this.outputDebug("[ Traklib :: callFunctionOnTracker]: "+e.valueType);
		}

		private function trackingChanged(e:TrakUIEvent):void {
			this.setTrackingType(e.valueType);
			this.__source.filters = [new ColorMatrixFilter(this.__matrix.getMatrix())];
		}

		protected function init():void {
			this.initDefaultConfig();
			this.initUI();
			this.initCamera();
			
			
			this.__matrix = new TrakColourMatrix();
			this.__video = new BitmapData(this.__trakUI.sourceWidth, this.__trakUI.sourceHeight, false, 0);
			this.__source = new Bitmap(this.__video);
			this.__source.scaleX = -1;
			this.__source.x = this.__trakUI.sourceWidth;
			this.__source.filters = [new ColorMatrixFilter(this.__matrix.getMatrix())];
		}
		
		protected function outputDebug(_text:String):void {
			if(this.__trakUI && this.__debug) {
				this.__trakUI.outputDebug(_text);
				trace(_text);
			}
		}
		protected function initDefaultConfig():void {
			TrakConfiguration.reset();
		}

		public function startTracking():void {
			this.__timer = new Timer(1000 / this.__frameRate, 0);
			this.__timer.addEventListener(TimerEvent.TIMER, this.track);
			this.__timer.start();
		}

		public function stopTracking():void {
			if(this.__timer) {
				this.__timer.stop();
				this.__timer.removeEventListener(TimerEvent.TIMER, this.track);
				this.__timer = null;
			}
		}

		public function openInterface():void {
			this.__trakUI.open();
		}

		public function showInterace(_show:Boolean = false):void {
			if(!this.__trakUI.parent) this.__refStage.addChild(this.__trakUI);
			if(_show) this.__trakUI.open();
		}

		public function hideInterace():void {
			if(this.__trakUI.parent) this.__trakUI.parent.removeChild(this.__trakUI);
		}

		private function track( e:TimerEvent ):void {
			this.__tracker.track();
			if(this.__tracker.motionArea) {
				if(this.__tracker.hasMovement) {
					var realCoordinates:Rectangle = new Rectangle(this.__tracker.motionArea.x, this.__tracker.motionArea.y, this.__tracker.motionArea.width, this.__tracker.motionArea.height);
					realCoordinates.x = ((this.__refStage.stage.stageWidth * realCoordinates.x) / this.__trakUI.sourceWidth);
					realCoordinates.y = ((this.__refStage.stage.stageHeight * realCoordinates.y) / this.__trakUI.sourceHeight);
						
					this.dispatchEvent(new TrakEvent(TrakEvent.MOVE, realCoordinates));
				}
			}
			
			if(this.__trakUI.openned) {
				this.__video.draw(this.__tracker.input);
				if(this.__tracker.hasMovement) {
					this.__trakUI.updateUI(this.__tracker.motionArea);
				}
			}
		}
		
		public function changeValue(_valueType:String, _value:int):void {
			switch( _valueType ) {
				case TrakConfiguration.BLUR :
					this.__tracker.blur = TrakConfiguration.blur = _value;
					break;
				
				case TrakConfiguration.BRIGHTNESS :
					this.__tracker.brightness = TrakConfiguration.brightness = this.__matrix.brightness = _value;
					break;
					
				case TrakConfiguration.SATURATION :
					this.__tracker.brightness = TrakConfiguration.saturation = this.__matrix.saturation = _value;
					break;
				
				case TrakConfiguration.CONTRAST :
					this.__tracker.contrast = TrakConfiguration.contrast = this.__matrix.contrast = _value;
					break;
				
				case TrakConfiguration.MIN_BLOB:
					this.__tracker.minBlob = TrakConfiguration.minBlob = _value;
					break;
					
				case TrakConfiguration.MAX_BLOB:
					this.__tracker.maxBlob = TrakConfiguration.maxBlob = _value;
					break;
					
				case TrakConfiguration.MULTI_TRACKING:
					this.__tracker.multiTracking = TrakConfiguration.multiTracking = (_value == 1) ? true:false;
					break;
					
					
				
				case TrakConfiguration.COLOR_SENSITIVITY:
					if(this.__tracker is TrakColor) {
						TrakColor(this.__tracker).colorSensitivity = TrakConfiguration.colorSensitivity = _value;
						return;
					}
					break;
			}
			this.__source.filters = [new ColorMatrixFilter(this.__matrix.getMatrix())];
			
			this.outputDebug("[ Traklib :: changeValue] _valueType:"+_valueType+" | _value:"+_value);
		}
		
		private function valuesChanged( e:TrakUIEvent ):void {
			e.stopImmediatePropagation();
			this.changeValue(e.valueType, e.value);
		}
		
		
		public function set debug(_debug:Boolean):void {
			__debug = _debug;
		}
	}
}
